use ash::vk::{self, ImageUsageFlags, SurfaceFormatKHR};

use crate::{VK_LOGV, VK_CHECK_UNSAFE};
use crate::{vulkan::VkContext, VK_LOGP};

use super::{VkSwapchain, VkSwapchainSupport};

fn choose_swapchain_format(available_formats: &Vec<SurfaceFormatKHR>) -> SurfaceFormatKHR {
    for available_format in available_formats.iter() {
        if available_format.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR && available_format.format == vk::Format::B8G8R8A8_SRGB {
            return available_format.clone();
        }
    }
    available_formats[0]
}

impl VkSwapchain {
    fn get_swapchain_ci(vk_context: &VkContext, device_swapchain_support: &VkSwapchainSupport) -> vk::SwapchainCreateInfoKHR {
        // Image Format
        if vk_context.verbose_log {
            VK_LOGV!("Surface Formats");
            for format in &device_swapchain_support.surface_formats {
                VK_LOGV!("    {:?}", format);
            }
            VK_LOGV!("    {:?}", device_swapchain_support.surface_capabilities);
        }
        let swapchain_format = choose_swapchain_format(&device_swapchain_support.surface_formats);

        vk::SwapchainCreateInfoKHR {
            s_type              : ash::vk::StructureType::SWAPCHAIN_CREATE_INFO_KHR,
            // p_next,
            // flags,
            surface             : vk_context.vk_surface.handler,
            min_image_count     : device_swapchain_support.surface_capabilities.min_image_count + 1,
            image_format        : swapchain_format.format,
            image_color_space   : swapchain_format.color_space,
            image_extent        : device_swapchain_support.surface_capabilities.current_extent,
            image_array_layers  : 1,                                    // todo 这里是给Swapchain输出image数量？
            image_usage         : ImageUsageFlags::COLOR_ATTACHMENT,    // todo 这里难道会控制Swapchain给出来的image类型么
            // image_sharing_mode,
            // queue_family_index_count,
            // p_queue_family_indices,
            pre_transform       : device_swapchain_support.surface_capabilities.current_transform,
            composite_alpha     : vk::CompositeAlphaFlagsKHR::OPAQUE,   // todo 也不知道作用，屏幕混合参数？
            // present_mode,
            // clipped,
            // old_swapchain

            ..Default::default()
        }
    }

    /// Swapchain的初始化逻辑比Image就多了一个拿到Swapchain
    /// 后续的都是类似Image，甚至可以直接拿到Image去创建ImageView
    pub fn new(vk_context: &VkContext) -> Self {
        let vk_device           = &vk_context.vk_device;
        let vk_surface          = &vk_context.vk_surface;
        let vk_instance         = &vk_context.vk_instance;
        let vk_physical_device  = &vk_context.vk_physical_device;

        let vk_swapchain_function_set = ash::extensions::khr::Swapchain::new(vk_instance, vk_device);

        // Create Swapchain
        let device_swapchain_support    = vk_surface.query_device_swapchain_support(vk_physical_device);
        let swapchain_ci                = Self::get_swapchain_ci(vk_context, &device_swapchain_support);

        let vk_swapchain = match VK_CHECK_UNSAFE!(vk_swapchain_function_set.create_swapchain(&swapchain_ci, None)) {
            Some(inner_value)   => inner_value,
            None                => VK_LOGP!("Failed to create_swapchain")
        };

        // Get Swapchain Image
        let swapchain_images = match VK_CHECK_UNSAFE!(vk_swapchain_function_set.get_swapchain_images(vk_swapchain)) {
            Some(inner_value)   => inner_value,
            None                => VK_LOGP!("Failed to get_swapchain_images")
        };
        if vk_context.verbose_log {
            VK_LOGV!("swapchain_images.len(): {}", swapchain_images.len());
        }

        // Create Swapchain ImageView
        let used_format = choose_swapchain_format(&device_swapchain_support.surface_formats);
        let mut swapchain_image_views = vec![];
        for swapchain_image in &swapchain_images {
            let image_view = vk_context.create_image_view_2d(swapchain_image, &used_format.format, &vk::ImageAspectFlags::COLOR);
            swapchain_image_views.push(image_view);
        }

        // Create Swapchain Image Memory Barrier
        vk_context.images_transition_layout(&swapchain_images, &used_format.format, vk::ImageLayout::UNDEFINED, vk::ImageLayout::PRESENT_SRC_KHR);
        
        Self {
            handler                 : vk_swapchain,
            function_set            : vk_swapchain_function_set,

            images                  : swapchain_images,
            device_support          : device_swapchain_support,

            used_format             : used_format,
            image_views             : swapchain_image_views
        }
    }

    pub fn get_extent(&self) -> vk::Extent2D { 
        self.device_support.surface_capabilities.current_extent 
    }

    /// 释放对应的Handler
    fn destory_resources(&mut self, vk_context: &VkContext) {
        for image_view in &self.image_views {
            unsafe{ vk_context.vk_device.destroy_image_view(*image_view, None)};
        }
        unsafe{ self.function_set.destroy_swapchain(self.handler, None) };
    }

    pub fn recreate(&mut self, vk_context: &VkContext) {
        let vk_device           = &vk_context.vk_device;
        let vk_surface          = &vk_context.vk_surface;
        let vk_instance         = &vk_context.vk_instance;
        let vk_physical_device  = &vk_context.vk_physical_device;

        // 首先释放资源
        self.destory_resources(vk_context);
        // self.function_set = ash::extensions::khr::Swapchain::new(vk_instance, vk_device);

        // Create Swapchain
        let device_swapchain_support    = vk_surface.query_device_swapchain_support(vk_physical_device);

        let swapchain_ci                = Self::get_swapchain_ci(vk_context, &device_swapchain_support);
        self.handler = match VK_CHECK_UNSAFE!(self.function_set.create_swapchain(&swapchain_ci, None)) {
            Some(inner_value)   => inner_value,
            None                => VK_LOGP!("Failed to create_swapchain")
        };

        // Get Swapchain Image
        self.images = match VK_CHECK_UNSAFE!(self.function_set.get_swapchain_images(self.handler)) {
            Some(inner_value)   => inner_value,
            None                => VK_LOGP!("Failed to get_swapchain_images")
        };

        // Create Swapchain ImageView
        let used_format = choose_swapchain_format(&device_swapchain_support.surface_formats);
        self.image_views = vec![];
        for swapchain_image in &self.images {
            let image_view = vk_context.create_image_view_2d(swapchain_image, &used_format.format, &vk::ImageAspectFlags::COLOR);
            self.image_views.push(image_view);
        }

        // Create Swapchain Image Memory Barrier
        vk_context.images_transition_layout(&self.images, &used_format.format, vk::ImageLayout::UNDEFINED, vk::ImageLayout::PRESENT_SRC_KHR);

        // Reset Swapchain Support Info
        self.device_support = device_swapchain_support;
    }
}

impl VkSwapchainSupport {
    pub fn debug_print(&self) {
        VK_LOGV!("    surface_formats {}", self.surface_formats.len());
        for surface_format in &self.surface_formats {
            VK_LOGV!("        {:?}", surface_format);
        }

        VK_LOGV!("    surface_present_modes {}", self.surface_present_modes.len());
        for surface_present_modes in &self.surface_present_modes {
            VK_LOGV!("        {:?}", surface_present_modes);
        }
        
        VK_LOGV!("    surface_capabilities");
        VK_LOGV!("        {:?}", self.surface_capabilities);
    }

    pub fn is_valid(&self) -> bool {
        self.surface_formats.len() > 0 && self.surface_present_modes.len() > 0
    }
}